#pragma once
#ifndef __G_IMAGE_H__
#define __G_IMAGE_H__


#include "GTypes.h"
#include "GTexture.h"
#include "GPixel.h"
#include "GError.h"

namespace GEngine
{
	class GImageCoord
	{
	public:
		GUInt32 X;
		GUInt32 Y;
		GUInt32 Z;

		GImageCoord() : X(0), Y(0), Z(0) {}
		GImageCoord(GUInt32 X, GUInt32 Y, GUInt32 Z) : X(X), Y(Y), Z(Z) {}
		~GImageCoord() {}
	};

	class GImageDimension
	{
	public:
		GUInt32	W;
		GUInt32	H;
		GUInt32	D;

		GImageDimension() : W(0), H(0), D(0) {}
		GImageDimension(GUInt32 W, GUInt32 H, GUInt32 D) : W(W), H(H), D(D) {}
		~GImageDimension() {}

		GSize GetPixelCount() const { return W * H * D; }
	};

	// Image class for manipulating uncompressed image formats
	class GImage
	{
	private:
		GUInt8*			Buffer;
		GSize			Size;

		GTextureFormat	Format;

		GImageDimension	Dimension;
		GUInt8			Bpp;

	public:
						GImage();
						~GImage();

		GSize			GetBpp() const;
		GSize			GetSize() const;
		GSize			GetWidth() const;
		GSize			GetHeight() const;
		GSize			GetDepth() const;
		GSize			GetPitch() const;
		GTextureFormat	GetFormat() const;
		GSize			GetDepthPitch() const;
		GUInt8*			GetBuffer(GSize Depth) const;

		void			CopyTo(const GImageCoord& SourceCoord, const GImageDimension& CopyDimension, const GImageCoord& TargetCoord, GImage* TargetImage);
		void			CopyFrom(const GImageCoord& TargetCoord, const GImageDimension& SourceDimension, const GImageCoord& SourceCoord, GUInt8* SourceImage);

		GUInt8*			Release(bool ReclaimBuffer = false);
		bool			Create(const GImageDimension& ImageDimension, GTextureFormat ImageFormat);
		bool			Create(const GImageDimension& ImageDimension, GTextureFormat ImageFormat, GUInt8* ImageBuffer);

		static GUInt8	GetFormatBpp(GTextureFormat Format);

		template<GTextureFormat TextureFormat>
		void Fill(const GPixel<TextureFormat>& Pixel) const
		{
			GUtil::GError::DebugCheck(TextureFormat != Format, "Pixel format does not match.");

			GPixel<TextureFormat>* PixelPtr = (GPixel<TextureFormat>*)Buffer;
			GSize PixelCount = Dimension.GetPixelCount();
			for (GSize I = 0; I < PixelCount; ++I)
			{
				*PixelPtr = Pixel;
				PixelPtr++;
			}
		}

		template<GTextureFormat TextureFormat>
		void SetPixel(const GImageCoord& Pos, const GPixel<TextureFormat>& Pixel)
		{
			GUtil::GError::DebugCheck(TextureFormat != Format, "Pixel format does not match.");

			GSize Offset = Dimension.W * Dimension.H * Pos.Z + Dimension.W * Pos.Y + Pos.X;
			GPixel<TextureFormat>* TargetPixel = (GPixel<TextureFormat>*)Buffer + Offset;

			*TargetPixel = Pixel;
		}

		template<GTextureFormat TextureFormat>
		GPixel<TextureFormat>& GetPixel(const GImageCoord& Pos)
		{
			GUtil::GError::DebugCheck(TextureFormat != Format, "Pixel format does not match.");

			GSize Offset = Dimension.W * Dimension.H * Pos.Z + Dimension.W * Pos.Y + Pos.X;
			GPixel<TextureFormat>* TargetPixel = (GPixel<TextureFormat>*)Buffer + Offset;

			return *TargetPixel;
		}
	};

}

#endif